﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Algobox.Objects.Finance.Orders;
using Algobox.Feeds.Finance.Trading;
using Algobox.Structure.Finance.Strategies.SmartStoploss;
using System.Threading;
using Algobox.Objects.Finance.Assets.Stock;
using Algobox.Objects.Finance.Prices;

namespace Algobox.Structure.Finance.Strategies.SpikeArb
{
    public class SpikeArbLegListing : Strategy, IOrderEvent, ISmartStoplossCallback
    {
        #region Constructors

        internal SpikeArbLegListing(SpikeArbLeg leg, SpikeArbStockListing listing)
            :base (listing.Listing.Security)
        {
            _isEnabled = true;
            _leg = leg;
            _legListing = listing;
            _rejectCount = 0;
            _phaseBuy = Phase.Idle;
            _phaseSell = Phase.Idle;
            _tradingFeed = _leg.Manager.Manager.TradingFeed;
            listing.OnEnabledChanged += new Action<bool>(OnEnabledChanged);
            ChangeExits(SpikeArbManagement.DEFAULT_EXIT_PERCENTAGE);

            _logId = base.Id;
            _strategyDetails = listing.Listing.ToString() + "|" + leg.Collar.ToString() + "|" + leg.Value + "@" + leg.Percentage + "%";

            if (Log.IsInfoEnabled)
                Log.Info("{0}|SpikeArbLegListing Creating new strategy with {1}", _logId, _strategyDetails);
            
            _last = listing.Listing.Security.Prices.Last;
        }

        #endregion
        //////////////////
        #region Member Variables

        /// <summary>
        /// Logger used by this class
        /// </summary>
        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

        private string _logId;
        private string _strategyDetails;

        /// <summary>
        /// Maximum of 3 rejections in a row
        /// </summary>
        private const int MAX_REJECTIONS = 3;

        /// <summary>
        /// Difference of 10% between stoploss and profit
        /// </summary>
        private const double DIFF_STOPLOSS_PCT = 0.1;

        /// <summary>
        /// Try and re-enter orders 3 times before disabling
        /// </summary>
        private const int MAX_ORDER_RETRIES = 3;

        private int _rejectCount;

        private bool _isEnabled;

        private Phase _phaseBuy;
        private Phase _phaseSell;
        private ITradingFeedAdaptor _tradingFeed;

        PriceVolume _last;
        
        private double _priceProfitBuy;
        private double _priceProfitSell;

        private double _exitPercentage1;
        private double _exitPercentage2;

        private SpikeArbLeg _leg;
        private SpikeArbStockListing _legListing;

        private Order _orderEntryBuy;
        private Order _orderEntrySell;

        private SmartStoplossOrder _orderExitBuy1;
        private SmartStoplossOrder _orderExitBuy2;

        private SmartStoplossOrder _orderExitSell1;
        private SmartStoplossOrder _orderExitSell2;

        private SpikeArbFill _fillBuy1;
        private SpikeArbFill _fillBuy2;
        private SpikeArbFill _fillSell1;
        private SpikeArbFill _fillSell2;


        #endregion
        //////////////////
        #region Properties
        
        public bool IsLegEnabled { get { return _legListing.IsEnabled; } }
        public bool IsEnabled { get { return _isEnabled; } }

        public bool IsActiveOrderEntryBuy { get { lock (this) return _orderEntryBuy != null; } }
        public bool IsActiveOrderEntrySell { get { lock (this) return _orderEntrySell != null; } }

        public StockListing Stock { get { return _legListing.Listing; } }

        #endregion
        //////////////////
        #region Functions

        void OnEnabledChanged(bool isEnabled)
        {
            lock (this)
            {
                if (_isEnabled && !isEnabled)
                {
                    // close out positions of triggered orders
                    //if (_phaseSell == Phase.Triggered)
                    //{
                    //    if (_orderExitSell1 != null) _orderExitSell1.StopOrder();
                    //    if (_orderExitSell2 != null) _orderExitSell2.StopOrder();
                    //}
                    //if (_phaseBuy == Phase.Triggered)
                    //{
                    //    if (_orderExitBuy1 != null) _orderExitBuy1.StopOrder();
                    //    if (_orderExitBuy2 != null) _orderExitBuy2.StopOrder();
                    //}
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|OnEnabledChanged Disabling this collar", _logId);
                    CancelBuyCollar();
                    CancelSellCollar();

                    _isEnabled = false;
                }
                else if (!_isEnabled && isEnabled)
                {
                    // switching to active so reset reject counter                    
                    _rejectCount = 0;
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|OnEnabledChanged Enabling this collar with {1}", _logId, _strategyDetails);
                    _isEnabled = true;
                }
            }
        }        

        internal void AddOrUpdateSell(double profitPrice, double collarPrice, uint collarQuantity)
        {
            lock (this)
            {
                if (_legListing.IsEnabled && _isEnabled)
                {
                    string message;
                    if (_phaseSell == Phase.Idle && _phaseBuy != Phase.Triggered)
                    {
                        decimal orderPrice = _legListing.Listing.TickSize.Round(collarPrice);
                                                                        
                        if (_orderEntrySell == null)
                        {
                            // check that collar price is within maximum allowable values to avoid busted trades
                            if (collarPrice < _last.Price + (_last.Price * SpikeArbManagement.MAX_COLLAR_PERCENTAGE / 100))
                            {
                                collarQuantity = Algobox.Toolbox.Math.Random.RandomiseOrderQuantity(collarQuantity);

                                _orderEntrySell = new Order(SpikeArbManagement.Account, _legListing.Listing, false, orderPrice, collarQuantity, Order.OrderType.Limit, Order.TimeInForce.DAY);

                                if (Log.IsInfoEnabled)
                                    Log.Info("{0}|AddOrUpdateSell Adding new sell collar Order({1})", _logId, _orderEntrySell.ToString());

                                _phaseSell = Phase.OrderPending;
                                if (_tradingFeed.TrySubmitOrder(_orderEntrySell, this, out message))
                                {
                                    _priceProfitSell = profitPrice;
                                }
                                else
                                {
                                    _phaseSell = Phase.Idle;
                                    if (++_rejectCount >= MAX_REJECTIONS)
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateSell Failed to submit ({4}), disabling collar - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntrySell.ToString(), message, _orderEntrySell.Text, _logId, _rejectCount);
                                        // to many rejects on insert so disabling this collar
                                        _isEnabled = false;
                                        _phaseSell = Phase.Idle;
                                        _orderEntrySell = null;
                                        CancelBuyCollar();
                                        return;
                                    }
                                    else
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateSell Failed to submit ({4}) - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntrySell.ToString(), message, _orderEntrySell.Text, _logId, _rejectCount);
                                    }
                                }
                            }
                        }
                        else
                        {
                            // dont change quantity up
                            if (collarQuantity > _orderEntrySell.Quantity)
                                collarQuantity = _orderEntrySell.Quantity;

                            if (orderPrice == _orderEntrySell.Price)
                            {
                                if (Log.IsInfoEnabled)
                                    Log.Info("{0}|AddOrUpdateSell Ignoring replace as price is the same Order({1})", _logId, _orderEntrySell.ToString());
                                return;
                            }

                            // check that collar price is within maximum allowable values to avoid busted trades
                            if (collarPrice < _last.Price + (_last.Price * SpikeArbManagement.MAX_COLLAR_PERCENTAGE / 100))
                            {
                                if (Log.IsInfoEnabled)
                                    Log.Info("{0}|AddOrUpdateSell Changing sell collar to -{1}@{2} Order({3})", _logId, collarQuantity, orderPrice, _orderEntrySell.ToString());

                                _phaseSell = Phase.OrderPending;
                                if (_tradingFeed.TryReplaceOrder(_orderEntrySell, out message, orderPrice, collarQuantity))
                                {
                                    _priceProfitSell = profitPrice;
                                }
                                else
                                {
                                    _phaseSell = Phase.Idle;
                                    if (++_rejectCount >= MAX_REJECTIONS)
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateSell Failed to update ({4}), disabling collar - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntrySell.ToString(), message, _orderEntrySell.Text, _logId, _rejectCount);
                                        // to many rejects on insert so disabling this collarSmart
                                        _isEnabled = false;
                                        _phaseSell = Phase.Idle;
                                        CancelSellCollar();
                                        CancelBuyCollar();
                                        return;
                                    }
                                    else
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateSell Failed to update ({4}) - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntrySell.ToString(), message, _orderEntrySell.Text, _logId, _rejectCount);
                                    }
                                }
                            }
                            else
                            {
                                // pull sell collar
                                if (Log.IsInfoEnabled)
                                    Log.Info("{1}|AddOrUpdateSell Pulling sell collar because collar price {2} is more than maximum upper range {3} Order({0})", _orderEntryBuy.ToString(), _logId, collarPrice, _last.Price + (_last.Price * SpikeArbManagement.MAX_COLLAR_PERCENTAGE / 100));
                                CancelSellCollar();
                            }
                        }
                    }
                }
                else if (_orderEntrySell != null)
                {
                    CancelSellCollar();
                }
            }
        }

        internal void AddOrUpdateBuy(double profitPrice, double collarPrice, uint collarQuantity)
        {
            lock (this)
            {
                if (_legListing.IsEnabled && _isEnabled)
                {
                    string message;
                    if (_phaseBuy == Phase.Idle && _phaseSell != Phase.Triggered)
                    {
                        decimal orderPrice = _legListing.Listing.TickSize.Round(collarPrice);

                        // check price is in valid trading range to avoid busted trades
                        if (_orderEntryBuy == null)
                        {
                            if (collarPrice > _last.Price - (_last.Price * SpikeArbManagement.MAX_COLLAR_PERCENTAGE / 100))
                            {
                                collarQuantity = Algobox.Toolbox.Math.Random.RandomiseOrderQuantity(collarQuantity);

                                _orderEntryBuy = new Order(SpikeArbManagement.Account, _legListing.Listing, true, orderPrice, collarQuantity, Order.OrderType.Limit, Order.TimeInForce.DAY);

                                if (Log.IsInfoEnabled)
                                    Log.Info("{1}|AddOrUpdateBuy Adding new buy collar Order({0})", _orderEntryBuy.ToString(), _logId);

                                _phaseBuy = Phase.OrderPending;
                                if (_tradingFeed.TrySubmitOrder(_orderEntryBuy, this, out message))
                                {
                                    _priceProfitBuy = profitPrice;
                                }
                                else
                                {
                                    _phaseBuy = Phase.Idle;
                                    if (++_rejectCount >= MAX_REJECTIONS)
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateBuy Failed to submit ({4}), disabling collar - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntryBuy.ToString(), message, _orderEntryBuy.Text, _logId, _rejectCount);
                                        // to many rejects on insert so disabling this collar
                                        _isEnabled = false;
                                        _phaseBuy = Phase.Idle;
                                        _orderEntryBuy = null;
                                        CancelSellCollar();
                                        return;
                                    }
                                    else
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateBuy Failed to submit ({4}) - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntryBuy.ToString(), message, _orderEntryBuy.Text, _logId, _rejectCount);
                                    }
                                }
                            }
                        }
                        else
                        {
                            // dont change quantity up
                            if (collarQuantity > _orderEntryBuy.Quantity)
                                collarQuantity = _orderEntryBuy.Quantity;

                            if (collarQuantity == _orderEntryBuy.Quantity && orderPrice == _orderEntryBuy.Price)
                            {
                                if (Log.IsInfoEnabled)
                                    Log.Info("{0}|AddOrUpdateBuy Ignoring replace as quantity and price are the same Order({1})", _logId, _orderEntrySell.ToString());
                                return;
                            }

                            if (collarPrice > _last.Price - (_last.Price * SpikeArbManagement.MAX_COLLAR_PERCENTAGE / 100))
                            {
                                // change buy order
                                if (Log.IsInfoEnabled)
                                    Log.Info("{1}|AddOrUpdateBuy Changing buy collar Order({0})", _orderEntryBuy.ToString(), _logId);
                                
                                _phaseBuy = Phase.OrderPending;
                                if (_tradingFeed.TryReplaceOrder(_orderEntryBuy, out message, orderPrice, collarQuantity))
                                {
                                    _priceProfitBuy = profitPrice;
                                }
                                else
                                {
                                    _phaseBuy = Phase.Idle;
                                    if (++_rejectCount >= MAX_REJECTIONS)
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateBuy Failed to update ({4}), disabling collar - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntryBuy.ToString(), message, _orderEntryBuy.Text, _logId, _rejectCount);
                                        // to many rejects on insert so disabling this collar
                                        _isEnabled = false;
                                        _phaseBuy = Phase.Idle;
                                        CancelSellCollar();
                                        CancelBuyCollar();
                                        return;
                                    }
                                    else
                                    {
                                        if (Log.IsErrorEnabled)
                                            Log.Error("{3}|AddOrUpdateBuy Failed to update ({4}) - Order({0}) FailReason({1}) OrderMessage({2})", _orderEntryBuy.ToString(), message, _orderEntryBuy.Text, _logId, _rejectCount);
                                    }
                                }
                            }
                            else
                            {
                                // pull buy collar
                                if (Log.IsInfoEnabled)
                                    Log.Info("{1}|AddOrUpdateBuy Pulling buy collar because collar price {2} is less than maximum lower range {3} Order({0})", _orderEntryBuy.ToString(), _logId, collarPrice, _last.Price - (_last.Price * SpikeArbManagement.MAX_COLLAR_PERCENTAGE / 100));
                                CancelBuyCollar();
                            }
                        }
                    }
                }
                else if (_orderEntryBuy != null)
                {
                    CancelBuyCollar();
                }
            }
        }

        internal void CancelSellCollar()
        {
            lock (this)
            {
                if (_orderEntrySell != null)
                {
                    if (_phaseSell == Phase.Idle)
                    {
                        string message;
                        if (!_tradingFeed.TryCancelOrder(_orderEntrySell, out message))
                        {
                            if (Log.IsErrorEnabled)
                                Log.Error("{2}|CancelSellCollar Failed to cancel Order({0}) FailReason({1})", _orderEntrySell, message, _logId);
                        }
                    }
                    _phaseSell = Phase.CancelPending;
                }
            }
        }

        internal void CancelBuyCollar()
        {
            lock (this)
            {
                if (_orderEntryBuy != null)
                {
                    if (_phaseBuy == Phase.Idle)
                    {
                        string message;
                        if (!_tradingFeed.TryCancelOrder(_orderEntryBuy, out message))
                        {
                            if (Log.IsErrorEnabled)
                                Log.Error("{2}|CancelBuyCollar Failed to cancel Order({0}) FailReason({1})", _orderEntrySell, message, _logId);
                        }
                    }
                    _phaseBuy = Phase.CancelPending;
                }
            }
        }

        /// <summary>
        /// Exits a sell collar buy buying back at two percentages
        /// </summary>
        /// <param name="fillPrice"></param>
        /// <param name="quantity"></param>
        private void ExitSellCollar(double fillPrice, uint quantity)
        {
            uint quantity1 = (uint)(quantity / 3);
            uint quantity2 = quantity - quantity1;

            double difference = fillPrice - _priceProfitSell;
            double exitPrice1 = fillPrice - (difference * _exitPercentage1);
            double exitPrice2 = fillPrice - (difference * _exitPercentage2);
            double stopPrice1 = fillPrice + (difference * (_exitPercentage1 - DIFF_STOPLOSS_PCT));
            double stopPrice2 = fillPrice + (difference * (_exitPercentage2 - DIFF_STOPLOSS_PCT));

            if (Log.IsWarnEnabled)
                Log.Warn("{0}|ExitSellCollar Exiting sell of {1}@{2} with buy of {3}@{4} & {5}@{6}", _logId, quantity, fillPrice, quantity1, exitPrice1, quantity2, exitPrice2);

            //send the big one first
            _orderExitSell2 = _leg.Manager.Manager.StoplossManager.CreateSmartStoploss(this, this, SpikeArbManagement.Account, _legListing.Listing, true, exitPrice2, stopPrice2, quantity2);
            _fillSell2 = new SpikeArbFill(this, _orderExitSell2, false, _leg.Percentage, _exitPercentage2, quantity2, fillPrice, exitPrice2);
            _leg.Manager.Manager.InsertExecution(_fillSell2);

            _orderExitSell1 = _leg.Manager.Manager.StoplossManager.CreateSmartStoploss(this, this, SpikeArbManagement.Account, _legListing.Listing, true, exitPrice1, stopPrice1, quantity1);
            _fillSell1 = new SpikeArbFill(this, _orderExitSell1, false, _leg.Percentage, _exitPercentage1, quantity1, fillPrice, exitPrice1);
            _leg.Manager.Manager.InsertExecution(_fillSell1);

        }

        private void ExitBuyCollar(double fillPrice, uint quantity)
        {
            uint quantity1 = (uint)(quantity / 3);
            uint quantity2 = quantity - quantity1;

            double difference = fillPrice - _priceProfitBuy;
            double exitPrice1 = fillPrice - (difference * _exitPercentage1);
            double exitPrice2 = fillPrice - (difference * _exitPercentage2);
            double stopPrice1 = fillPrice + (difference * (_exitPercentage1 - DIFF_STOPLOSS_PCT));
            double stopPrice2 = fillPrice + (difference * (_exitPercentage2 - DIFF_STOPLOSS_PCT));

            if (Log.IsWarnEnabled)
                Log.Warn("{0}|ExitBuyCollar Exiting buy of {1}@{2} with sell of {3}@{4} & {5}@{6}", _logId, quantity, fillPrice, quantity1, exitPrice1, quantity2, exitPrice2);

            //send the big one first
            _orderExitBuy2 = _leg.Manager.Manager.StoplossManager.CreateSmartStoploss(this, this, SpikeArbManagement.Account, _legListing.Listing, false, exitPrice2, stopPrice2, quantity2);
            _fillBuy2 = new SpikeArbFill(this, _orderExitBuy2, true, _leg.Percentage, _exitPercentage2, quantity2, fillPrice, exitPrice2);
            _leg.Manager.Manager.InsertExecution(_fillBuy2);

            _orderExitBuy1 = _leg.Manager.Manager.StoplossManager.CreateSmartStoploss(this, this, SpikeArbManagement.Account, _legListing.Listing, false, exitPrice1, stopPrice1, quantity1);
            _fillBuy1 = new SpikeArbFill(this, _orderExitBuy1, true, _leg.Percentage, _exitPercentage1, quantity1, fillPrice, exitPrice1);
            _leg.Manager.Manager.InsertExecution(_fillBuy1);
        }


        public void OrderUpdate(Order order, Order.Status state)
        {
            if (Log.IsDebugEnabled)
                Log.Debug("{0}|OrderUpdate recieved order in state {1} Order({1})", _logId, state, order.ToString());

            lock (this)
            {
                switch (state)
                {
                    // order complete
                    case Order.Status.Filled:
                    case Order.Status.Canceled:
                    case Order.Status.Stopped:
                        if (order.QuantityFilled > 0)
                        {
                            // create sell orders to exit
                            if (order == _orderEntryBuy)
                            {                                
                                if (Log.IsInfoEnabled)
                                    Log.Info("{0}|OrderUpdate Triggered buy at {1} from {2} - Order({3})", _logId, order.Price, _priceProfitBuy, order.ToString());
                                _phaseBuy = Phase.Triggered;
                                ExitBuyCollar((double)_orderEntryBuy.Price, order.QuantityFilled);
                                CancelSellCollar();
                                _orderEntryBuy = null;
                            }
                            else if (order == _orderEntrySell)
                            {                                                                
                                if (Log.IsInfoEnabled)
                                    Log.Info("{0}|OrderUpdate Triggered sell at {1} from {2} - Order({3})", _logId, order.Price, _priceProfitSell, order.ToString());
                                _phaseSell = Phase.Triggered;
                                ExitSellCollar((double)_orderEntrySell.Price, order.QuantityFilled);
                                CancelBuyCollar();
                                _orderEntrySell = null;
                            }
                        }
                        else
                        {
                            // collar is in stable state so set side to idle
                            if (_orderEntryBuy == order)
                            {
                                if (Log.IsTraceEnabled)
                                    Log.Trace("{0}|OrderUpdate Completed sell order - Order({3})", _logId, order.Price, _priceProfitSell, order.ToString());
                                _phaseBuy = Phase.Idle;
                                _orderEntryBuy = null;
                            }
                            else
                            {
                                if (Log.IsTraceEnabled)
                                    Log.Trace("{0}|OrderUpdate Completed buy order - Order({3})", _logId, order.Price, _priceProfitSell, order.ToString());
                                _phaseSell = Phase.Idle;
                                _orderEntrySell = null;
                            }
                        }
                        // reset the reject count
                        _rejectCount = 0;

                        break;

                    // cancel a part filled order
                    case Order.Status.FilledPartial:
                        string message;
                        if (_tradingFeed.TryCancelOrder(order, out message))
                        {
                            if (_orderEntryBuy == order)
                            {
                                _phaseBuy = Phase.CancelPending;
                            }
                            else
                            {
                                _phaseSell = Phase.CancelPending;
                            }
                        }
                        else
                        {
                            if (Log.IsErrorEnabled)
                                Log.Error("{0}|OrderUpdate Failed to cancel part filled Order({1})", _logId, order.ToString());
                            CancelBuyCollar();
                            CancelSellCollar();
                        }
                        // reset the reject count
                        _rejectCount = 0;
                        break;

                    // deal with a reject or cancel the order of a replace reject
                    case Order.Status.Rejected:
                        if (Log.IsWarnEnabled)
                            Log.Warn("{0}|OrderUpdate Order rejected Order({1}) Message({2}) ", _logId, order.ToString(), order.Text);

                        // check previous state, failed replace so pull order 
                        if (order.State == Order.Status.PendingReplace)
                        {
                            if (_tradingFeed.TryCancelOrder(order, out message))
                            {
                                if (_orderEntryBuy == order)
                                {
                                    _phaseBuy = Phase.CancelPending;
                                }
                                else
                                {
                                    _phaseSell = Phase.CancelPending;
                                }
                            }
                        }
                        // failed adding new order
                        else if (order.State == Order.Status.PendingNew || order.State == Order.Status.New)
                        {
                            // keep count of how many times this happens
                            if (++_rejectCount >= MAX_REJECTIONS)
                            {
                                // to many rejects on insert so disabling this collar
                                _isEnabled = false;
                                if (_orderEntrySell == order)
                                {
                                    _phaseSell = Phase.Idle;
                                    _orderEntrySell = null;
                                    CancelBuyCollar();
                                }
                                else
                                {
                                    _phaseBuy = Phase.Idle;
                                    _orderEntryBuy = null;
                                    CancelSellCollar();
                                }
                                return;
                            }

                            // collar is in stable stage so set side to idle
                            if (_orderEntryBuy == order)
                            {
                                _phaseBuy = Phase.Idle;
                                _orderEntryBuy = null;
                            }
                            else
                            {
                                _phaseSell = Phase.Idle;
                                _orderEntrySell = null;
                            }
                        }
                        else
                        {
                            if (Log.IsErrorEnabled)
                                Log.Error("{0}|OrderUpdate Unhandled order state from PreviousState({1}) to NewState({2})", _logId, order.State.ToString(), state.ToString());
                            if (_orderEntryBuy == order)
                            {
                                if (_phaseBuy == Phase.CancelPending)
                                {
                                    _phaseBuy = Phase.Idle;
                                    CancelBuyCollar();
                                }
                            }
                            else
                            {
                                if (_phaseSell == Phase.CancelPending)
                                {
                                    _phaseSell = Phase.Idle;
                                    CancelSellCollar();
                                }
                            }
                        }


                        break;

                    // collar is in stable stage so set side to idle
                    case Order.Status.New:
                    case Order.Status.Replaced:
                    case Order.Status.Updated:
                        if (_orderEntryBuy == order)
                        {
                            if (Log.IsTraceEnabled)
                                Log.Trace("{0}|OrderUpdate Buy order changed from PreviousState({1}) to NewState({2}) ClOrdId({3})", _logId, order.State.ToString(), state.ToString(), order.OrderIdClient);
                            if (_phaseBuy == Phase.CancelPending)
                            {
                                _phaseBuy = Phase.Idle;
                                CancelBuyCollar();
                            }
                            else
                            {
                                _phaseBuy = Phase.Idle;
                            }
                        }
                        else if (_orderEntrySell == order)
                        {
                            if (Log.IsTraceEnabled)
                                Log.Trace("{0}|OrderUpdate Sell order changed from PreviousState({1}) to NewState({2}) ClOrdId({3})", _logId, order.State.ToString(), state.ToString(), order.OrderIdClient);
                            if (_phaseSell == Phase.CancelPending)
                            {
                                CancelSellCollar();
                                _phaseSell = Phase.Idle;
                            }
                            else
                            {
                                _phaseSell = Phase.Idle;
                            }
                        }
                        // reset the reject count
                        _rejectCount = 0;
                        break;

                    // otherwise wait for the correct state
                    default:
                        break;
                }
            } // end lock
        }

        public void OnSmartStoplossFill(SmartStoplossOrder order)
        {       
            if (order == _orderExitBuy1)
            {
                _fillBuy1.Remaining = order.QuantityRemaining;
                _fillBuy1.Realised = order.AveragePrice;
                if (Log.IsTraceEnabled)
                    Log.Trace("{0}|OnSmartStoplossFill Recieved fill for buy exit 1, with {1} remaining", _logId, order.QuantityRemaining);
            }
            else if (order == _orderExitBuy2)
            {
                _fillBuy2.Remaining = order.QuantityRemaining;
                _fillBuy2.Realised = order.AveragePrice;
                if (Log.IsTraceEnabled)
                    Log.Trace("{0}|OnSmartStoplossFill Recieved fill for buy exit 2, with {1} remaining", _logId, order.QuantityRemaining);
            }
            else if (order == _orderExitSell1)
            {
                _fillSell1.Remaining = order.QuantityRemaining;
                _fillSell1.Realised = order.AveragePrice;
                if (Log.IsTraceEnabled)
                    Log.Trace("{0}|OnSmartStoplossFill Recieved fill for sell exit 1, with {1} remaining", _logId, order.QuantityRemaining);
            }
            else if (order == _orderExitSell2)
            {
                _fillSell2.Remaining = order.QuantityRemaining;
                _fillSell2.Realised = order.AveragePrice;
                if (Log.IsTraceEnabled)
                    Log.Trace("{0}|OnSmartStoplossFill Recieved fill for sell exit 2, with {1} remaining", _logId, order.QuantityRemaining);
            }
        }

        public void OnSmartStoplossComplete(SmartStoplossOrder order)
        {
            if (order == _orderExitBuy1)
            {
                if (Log.IsInfoEnabled)
                    Log.Info("{0}|OnSmartStoplossComplete Completed exit for buy order 1", _logId);
                _orderExitBuy1 = null;
                if (_orderExitBuy2 == null)
                {
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|OnSmartStoplossComplete Re-enabling buy collar", _logId);
                    _phaseBuy = Phase.Idle;
                }
            }
            else if (order == _orderExitBuy2)
            {
                if (Log.IsInfoEnabled)
                    Log.Info("{0}|OnSmartStoplossComplete Completed exit for buy order 2", _logId);
                _orderExitBuy2 = null;
                if (_orderExitBuy1 == null)
                {
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|OnSmartStoplossComplete Re-enabling buy collar", _logId);
                    _phaseBuy = Phase.Idle;
                }
            }
            else if (order == _orderExitSell1)
            {
                if (Log.IsInfoEnabled)
                    Log.Info("{0}|OnSmartStoplossComplete Completed exit for sell order 1", _logId);
                _orderExitSell1 = null;
                if (_orderExitSell2 == null)
                {
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|OnSmartStoplossComplete Re-enabling sell collar", _logId);
                    _phaseSell = Phase.Idle;
                }
            }
            else if (order == _orderExitSell2)
            {
                if (Log.IsInfoEnabled)
                    Log.Info("{0}|OnSmartStoplossComplete Completed exit for sell order 2", _logId);
                _orderExitSell2 = null;
                if (_orderExitSell1 == null)
                {
                    if (Log.IsInfoEnabled)
                        Log.Info("{0}|OnSmartStoplossComplete Re-enabling sell collar", _logId);
                    _phaseSell = Phase.Idle;
                }
            }
        }

        #endregion
        //////////////////
        #region Enums

        internal void ChangeExits(SpikeArbStock.ExitPercentages value)
        {            
            switch (value)
            {
                case SpikeArbStock.ExitPercentages.P000_000:
                    _exitPercentage1 = 0;
                    _exitPercentage2 = 0;
                    break;
                case SpikeArbStock.ExitPercentages.P010_010:
                    _exitPercentage1 = 0.1;
                    _exitPercentage2 = 0.1;
                    break;
                case SpikeArbStock.ExitPercentages.P050_030:
                    _exitPercentage1 = 0.3;
                    _exitPercentage2 = 0.5;
                    break;
                case SpikeArbStock.ExitPercentages.P080_030:
                    _exitPercentage1 = 0.3;
                    _exitPercentage2 = 0.8;
                    break;
                case SpikeArbStock.ExitPercentages.P090_040:
                    _exitPercentage1 = 0.4;
                    _exitPercentage2 = 0.9;
                    break;
                case SpikeArbStock.ExitPercentages.P100_050:
                    _exitPercentage1 = 0.5;
                    _exitPercentage2 = 1;
                    break;
                case SpikeArbStock.ExitPercentages.P100_070:
                    _exitPercentage1 = 0.7;
                    _exitPercentage2 = 1;
                    break;
                case SpikeArbStock.ExitPercentages.P100_100:
                    _exitPercentage1 = 1;
                    _exitPercentage2 = 1;
                    break;
                case SpikeArbStock.ExitPercentages.P120_050:
                    _exitPercentage1 = 0.5;
                    _exitPercentage2 = 1.2;
                    break;
                case SpikeArbStock.ExitPercentages.P120_120:
                    _exitPercentage1 = 1.2;
                    _exitPercentage2 = 1.2;
                    break;
                default:
                    _exitPercentage1 = 0.4;
                    _exitPercentage2 = 0.9;
                    break;
            }
            if (Log.IsInfoEnabled)
                Log.Info("{0}|ChangeExits chaning exits to {1}% & {2}%", _logId, _exitPercentage1, _exitPercentage2);
        }

        private enum Phase
        {
            Idle,
            CancelPending,
            OrderPending,
            Triggered
        }

        #endregion

    }
}
